/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.web.io;

import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * This class wrapps a {@link HttpServletRequest} and allows to modify
 * the attributes and the parameters of the request without modifying the
 * underlying request.
 */
public class HttpServletRequestProxy
    extends HttpServletRequestWrapper
{

  /**
   * the attributes added to this request.
   */
  private transient final Map<String, Object> attributes = new TreeMap<String, Object>();

  /**
   * the attributes removed from this request.
   */
  private transient final Set<String> removedAttributes = new TreeSet<String>();

  /**
   * the parameters added to this request.
   */
  private transient final Map<String, String[]> parameters = new TreeMap<String, String[]>();

  /**
   * the parameters removed from this request.
   */
  private transient final Set<String> removedParameters = new TreeSet<String>();



  /**
   * Constructor.
   * 
   * @param request
   *          the wrapped <code>HttpServletRequest</code>
   */
  HttpServletRequestProxy(final HttpServletRequest request)
  {
    super(request);
  }



  /**
   * Returns the value of the named attribute as an <code>Object</code>, or
   * <code>null</code> if no attribute of the given name exists.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the attribute
   * @return an <code>Object</code> containing the value of the attribute, or
   *         <code>null</code> if the attribute does not exist
   * @see javax.servlet.ServletRequestWrapper#getAttribute(java.lang.String)
   */
  @Override
  public Object getAttribute(final String name)
  {
    return ProcessProxy.getValue(name, attributes, removedAttributes, super.getAttribute(name));
  }



  /**
   * Returns an <code>Enumeration</code> containing the names of the
   * attributes available to this request. This method returns an empty
   * <code>Enumeration</code> if the request has no attributes available to
   * it.
   * 
   * @return an <code>Enumeration</code> of strings containing the names of
   *         the request's attributes
   * @see javax.servlet.ServletRequestWrapper#getAttributeNames()
   */
  @SuppressWarnings("unchecked")
  @Override
  public Enumeration<String> getAttributeNames()
  {
    return ProcessProxy.getNames(attributes, removedAttributes, super.getAttributeNames());
  }



  /**
   * Returns the value of a request parameter as a <code>String</code>, or
   * <code>null</code> if the parameter does not exist. Request parameters are
   * extra information sent with the request. For HTTP servlets, parameters are
   * contained in the query string or posted form data.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the parameter
   * @return a <code>String</code> representing the single value of the
   *         parameter
   * @see javax.servlet.ServletRequestWrapper#getParameter(java.lang.String)
   */
  @Override
  public String getParameter(final String name)
  {
    final String[] res = getParameterValues(name);
    if (res != null) {
      return res[0];
    }
    return null;
  }



  /**
   * Returns a java.util.Map of the parameters of this request. Request
   * parameters are extra information sent with the request. For HTTP servlets,
   * parameters are contained in the query string or posted form data.
   * 
   * @return an immutable java.util.Map containing parameter names as keys and
   *         parameter values as map values. The keys in the parameter map are
   *         of type String. The values in the parameter map are of type String
   *         array.
   * @see javax.servlet.ServletRequestWrapper#getParameterMap()
   */
  @SuppressWarnings("unchecked")
  @Override
  public Map<String, String[]> getParameterMap()
  {
    return ProcessProxy.getValueMap(parameters, removedParameters, super.getParameterMap());
  }



  /**
   * Returns an <code>Enumeration</code> of <code>String</code> objects
   * containing the names of the parameters contained in this request. If the
   * request has no parameters, the method returns an empty
   * <code>Enumeration</code>.
   * 
   * @return an <code>Enumeration</code> of <code>String</code> objects,
   *         each <code>String</code> containing the name of a request
   *         parameter; or an empty <code>Enumeration</code> if the request
   *         has no parameters
   * @see javax.servlet.ServletRequestWrapper#getParameterNames()
   */
  @SuppressWarnings("unchecked")
  @Override
  public Enumeration<String> getParameterNames()
  {
    return ProcessProxy.getNames(parameters, removedParameters, super.getParameterNames());
  }



  /**
   * Returns an array of <code>String</code> objects containing all of the
   * values the given request parameter has, or <code>null</code> if the
   * parameter does not exist.
   * <p>
   * If the parameter has a single value, the array has a length of 1.
   * 
   * @param name
   *          a <code>String</code> containing the name of the parameter whose
   *          value is requested
   * @return an array of <code>String</code> objects containing the
   *         parameter's values
   * @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
   */
  @Override
  public String[] getParameterValues(final String name)
  {
    return ProcessProxy.getValue(name, parameters, removedParameters, super.getParameterValues(name));
  }



  /**
   * Removes an attribute from this request. This method is not generally needed
   * as attributes only persist as long as the request is being handled.
   * <p>
   * Attribute names should follow the same conventions as package names. Names
   * beginning with <code>java.*</code>, <code>javax.*</code>, and
   * <code>com.sun.*</code>, are reserved for use by Sun Microsystems.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the attribute to
   *          remove
   * @see javax.servlet.ServletRequestWrapper#removeAttribute(java.lang.String)
   */
  @Override
  public void removeAttribute(final String name)
  {
    attributes.remove(name);
    removedAttributes.add(name);
  }



  /**
   * Removes an parameter from this request.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the parameter to
   *          remove
   */
  public void removeParameter(final String name)
  {
    parameters.remove(name);
    removedParameters.add(name);
  }



  /**
   * Stores an attribute in this request. Attributes are reset between requests.
   * This method is most often used in conjunction with
   * <code>RequestDispatcher</code>.
   * <p>
   * Attribute names should follow the same conventions as package names. Names
   * beginning with <code>java.*</code>, <code>javax.*</code>, and
   * <code>com.sun.*</code>, are reserved for use by Sun Microsystems. If the
   * object passed in is <code>null</code>, the effect is the same as calling
   * <code>removeAttribute(java.lang.String)</code>.
   * <p>
   * It is warned that when the request is dispatched from the servlet resides
   * in a different web application by RequestDispatcher, the object set by this
   * method may not be correctly retrieved in the caller servlet.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the attribute
   * @param value
   *          the <code>Object</code> to be stored
   * @see javax.servlet.ServletRequestWrapper#setAttribute(java.lang.String,
   *      java.lang.Object)
   */
  @Override
  public void setAttribute(final String name, final Object value)
  {
    attributes.put(name, value);
    removedAttributes.remove(name);
  }



  /**
   * Stores an array of String objects containing all of the values the given
   * request parameter.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the parameter
   * @param value
   *          the array of <code>String</code> to be stored
   */
  public void setParameter(final String name, final String value[])
  {
    parameters.put(name, value);
    removedParameters.remove(name);
  }



  /**
   * Stores a parameter in this request.
   * 
   * @param name
   *          a <code>String</code> specifying the name of the parameter
   * @param value
   *          the <code>String</code> to be stored
   */
  public void setParameter(final String name, final String value)
  {
    setParameter(name, new String[] { value });
  }

}
